package top.healthylife.gateway.platform.baihang.api.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import top.healthylife.basics.utils.exception.BusinessException;
import top.healthylife.basics.utils.utils.SystemThreadLocal;
import top.healthylife.gateway.core.util.SystemConfigUtils;
import top.healthylife.gateway.dto.request.req.ApiReqDto;
import top.healthylife.gateway.platform.baihang.api.config.SystemConfig;
import top.heathylife.gateway.common.cons.SystemCons;
import top.heathylife.gateway.common.po.RecordRequestInfo;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.HashMap;
import java.util.Map;


/**
 * 百行征信统一接口调用工具类
 *
 * @author gwx
 * @date 2021/9/8
 */
@Slf4j
@SuppressWarnings("all")
public class BaiHangHttpUtil {

    private final static String SIT_URL = "https://rds-t.baihangcredit.com/openapi/queryData/";
    private final static String PRO_URL = "https://rds.baihangcredit.com/openapi/queryData/";
    public static final String KEY_TYPE = "3DES";
    public static Map<String, String> codeMap = new HashMap<>();

    static {
        codeMap.put("0000", "查询成功");
        codeMap.put("1001", "参数错误");
        codeMap.put("1002", "身份认证失败");
        codeMap.put("1003", "权限不足");
        codeMap.put("1004", "用户状态异常");
        codeMap.put("1014", "用户不存在或被锁定");
        codeMap.put("1005", "查询请求标识重复");
        codeMap.put("1006", "用户流控超过配置上限");
        codeMap.put("1007", "参数无匹配记录");
        codeMap.put("2001", "查询失败");
        codeMap.put("2002", "数据源无数据");
        codeMap.put("4001", "产品未发布");
        codeMap.put("5000", "服务错误");
        codeMap.put("5001", "接口暂时无法访问");
        codeMap.put("5005", "数据源负载过高");
    }

    /**
     * 百行征信-统一请求入口
     *
     * @param reqDto
     * @param productCode 接口名称,百行接口名称为产品编号,如 MBASU001
     * @param res
     * @return
     */
    @SneakyThrows
    public static <T> T httpPostRequest(ApiReqDto reqDto, String productCode, Class<T> res) {
        //获取账号配置信息
        SystemConfig systemConfig = systemConfigInfo(reqDto);
        //构造请求参数
        RequestParameter requestParameter = buildReqParam(systemConfig, reqDto);
        String queryString = requestParameter.toEncryptString(KEY_TYPE);
        String url = getUrl(systemConfig.getEnv());
        //将请求参数信息存储到线程变量
        RecordRequestInfo.recordRequest(url, queryString, null, "post");
        String body = HttpUtil.createPost(url + productCode).body(queryString).execute().body();
        JSONObject jsonObject = JSONObject.parseObject(body);
        String result = jsonObject.getJSONObject("head").getString("result");
        String response = jsonObject.getString("response");
        if ("Y".equals(result)) {
            //返回结果如果是Y,返回res对象
            log.info("百行调用接口成功,产品编号为:{},请求参数为:{}", productCode, JSON.toJSONString(reqDto));
            String decryptResponse = decryptResponse(response, KEY_TYPE, systemConfig.getSecretKey());
            //将请求结果信息存储到线程变量
            SystemThreadLocal.set(SystemCons.ORIGIN_DATA, decryptResponse);
            return JSONObject.parseObject(decryptResponse, res);
        }
        String responseCode = jsonObject.getJSONObject("head").getString("responseCode");
        String responseMsg = jsonObject.getJSONObject("head").getString("responseMsg");
        throw new BusinessException("百行接口响应异常:{},code码:{}", responseMsg, responseCode);
    }

    private static String getUrl(String env) {
        switch (env) {
            case "SIT":
                return SIT_URL;
            case "PRO":
                return PRO_URL;
            default:
                throw new BusinessException("未知的api访问环境");
        }
    }

    /**
     * 构造百行请求参数
     *
     * @param systemConfig 系统参数
     * @param reqDto       接口请参
     * @return
     */
    public static RequestParameter buildReqParam(SystemConfig systemConfig, ApiReqDto reqDto) throws Exception {
        RequestParameter requestParameter = new RequestParameter();
        //请求号前缀标识符号可根据各机构自定义设置("机构简称")
        String secretId = systemConfig.getSecretId();
        String secretKey = systemConfig.getSecretKey();
        Map<String, Object> paraMap = BeanUtil.beanToMap(reqDto);
        //0:明文（不传时默认为明文）； 1：MD5； 2：SHA256； 3：SM3
        paraMap.put("encryptType", "0");
        // 用户查询请求标示，要求每次（接入方自设）
        String requestRefId = IdUtil.fastSimpleUUID();
        String beforeSign = "requestRefId=" + requestRefId + "&secretId=" + secretId;
        byte[] keyBytes = Base64.decode(secretKey);
        SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");
        Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(signingKey);
        byte[] rawHmac = mac.doFinal(beforeSign.getBytes());
        String signature = Base64.encode(rawHmac);
        RequestParameter.Head head = requestParameter.new Head(requestRefId, secretId, signature);
        RequestParameter.Request request = requestParameter.new Request(paraMap);
        requestParameter.setSecretKey(secretKey);
        requestParameter.setHead(head);
        requestParameter.setRequest(request);
        return requestParameter;
    }


    /**
     * 将待签名明文和密钥使用HMAC-SHA1生成签名
     *
     * @param staySignature 待签名明文
     * @param secretKey     密钥
     * @return
     */
    private static String createSignature(String staySignature, String secretKey) throws Exception {
        byte[] keyBytes = Base64.decode(secretKey);
        SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");
        Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(signingKey);
        byte[] rawHmac = mac.doFinal(staySignature.getBytes());
        return Base64.encode(rawHmac);
    }

    /**
     * 将请求报文对象加密成一个字符串
     *
     * @param object
     * @param secretKey 百行提供的密钥
     * @return
     */
    @SneakyThrows
    private static String encode3DES(Object object, String secretKey) {
        //将密钥进行base64解码
        //byte[] secretKeyByte = Base64.decode(secretKey);
        if (ObjectUtil.isEmpty(object)) {
            throw new BusinessException("请求参数有误");
        } else {
            //服务端和客户端之间交换的报文默认采用 3DES 对称加密。客户端提交请求报文时，须
            //对 request 下的内容做 3DES 对称加密，加密模式使用 ECB，填充算法使用 pkcs5padding，
            //加密密钥为 base64 解码后的 secretKey
            return SecureUtil.desede(secretKey.getBytes()).encryptBase64(JSONObject.toJSONString(object));
        }
    }

    /**
     * 将响应报文解密成一个对象
     *
     * @param responseParam
     * @param secretKey     百行提供的密钥
     * @return
     */
    @SneakyThrows
    private static <T> T decode3DES(String responseParam, String secretKey, Class<T> res) {
        if (StringUtils.isEmpty(secretKey)) {
            throw new BusinessException("密钥不存在,请联系管理员处理");
        }
        if (StringUtils.isEmpty(responseParam)) {
            throw new BusinessException("响应参数有误");
        } else {
            //服务端和客户端之间交换的报文默认采用 3DES 对称加密。客户端提交请求报文时，须
            //对 request 下的内容做 3DES 对称加密，加密模式使用 ECB，填充算法使用 pkcs5padding，
            //加密密钥为 base64 解码后的 secretKey
            return JSON.parseObject(Des3Util.decode(responseParam, secretKey), res);
        }
    }


    public static String decryptResponse(String response, String keyType, String key) throws Exception {

        if (keyType.toLowerCase().equals("3des") || keyType.toLowerCase().equals("desede")) {
            DESedeKeySpec dks = new DESedeKeySpec(Base64.decode(key));
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
            SecretKey secretKey = keyFactory.generateSecret(dks);

            Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            byte[] b = cipher.doFinal(Base64.decode(response));
            String param = new String(b, "utf-8");

            return param;
        }
        return null;
    }


    /**
     * 访问日志输出和配置获取
     *
     * @param apiReqDto 入参
     * @return 账号信息
     */
    public static SystemConfig systemConfigInfo(ApiReqDto apiReqDto) {
        //可直接通过工具类获取这个对象
        SystemConfig o = (SystemConfig) SystemConfigUtils.get();
        log.info("请求参数为:{}", JSONObject.toJSONString(apiReqDto));
        log.info("账号信息为:{}", JSONObject.toJSONString(o));
        return o;
    }
//
//    public static void main(String[] args) {
//        String content = "requestRefId=1a31ffd8-7a3a-4ad2-a80d-b85fc2c1b1b0&secretId=TLJKJ_J0001";
//        String secretKey = "EJShl1fTNzRKlBBkNJuMRY/lzZs9wQdn";
//        SecretKey deSede = SecureUtil.generateDESKey("DESede", secretKey.getBytes());
//        DESede desede = SecureUtil.desede(deSede.getEncoded());
//        String s = desede.encryptBase64(content);
//        System.out.println("加密后: = " + s);
//        SecretKey deSede2 = SecureUtil.generateDESKey("DESede", secretKey.getBytes());
//        DESede desede2 = SecureUtil.desede(deSede2.getEncoded());
//        String s1 = desede2.decryptStr(s);
//        System.out.println("s1 = " + s1);
//    }
}
